package com.tfswx.ocrengine.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.lowagie.text.Document;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfWriter;
import com.tfswx.ocrengine.dto.OcrOutputDTO;
import com.tfswx.ocrengine.exception.AppException;
import com.tfswx.ocrengine.executor.bean.PrismWordsInfo;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

/**
 * @author Ricky
 * @date 2021/5/27
 */
@Slf4j
public final class PdfBase64StrTool {
    /**
     * 全⻆角半⻆角符号转化之间的间隔
     */
    public static final char DBC_SBC_STEP = 65248;

    private PdfBase64StrTool() {
    }


    /**
     * 解析阿里识别结果生成PDF文件Base64字符串数据
     *
     * @param ocrOutputDTO
     * @param imgData
     * @return
     */
    public static byte[] getPdfBytes(OcrOutputDTO ocrOutputDTO, byte[] imgData) throws IOException {
        int angle = 0;
        JSONArray prismWordsInfo = ocrOutputDTO.getPrismWordsInfo();

        List<PrismWordsInfo> prismWordsInfos = prismWordsInfo.toJavaList(PrismWordsInfo.class);
        // ⽂文字透明度
        float opacity = 0F;
        return generatePdfFile(imgData, prismWordsInfos, angle, opacity);
    }

    /**
     * ⽣生成pdf⽂文件
     *
     * @param imgData         图⽚片内容
     * @param prismWordsInfos 图⽚文本信息
     * @param angle           图⽚片旋转⻆角⻆角度
     * @param opacity         ⽂文字透明度
     * @throws IOException
     */
    private static byte[] generatePdfFile(byte[] imgData, List<PrismWordsInfo> prismWordsInfos, Integer angle, float opacity) throws IOException {
        Document pdfDocument = new Document();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        PdfWriter pdfWriter = PdfWriter.getInstance(pdfDocument, stream);
        scPdf(imgData, pdfDocument, pdfWriter, angle);

        setTextToPdf(pdfWriter, prismWordsInfos, opacity);

        pdfDocument.close();
        return stream.toByteArray();
    }

    /**
     * ⽣生成pdf
     *
     * @param imgData     图⽚片内容
     * @param pdfDocument pdfDocument
     * @param angle
     * @throws IOException
     */
    private static void scPdf(byte[] imgData, Document pdfDocument, PdfWriter pdfWriter, Integer angle) throws IOException {
        // 旋转后的宽度
        int width;
        // 旋转后的⾼度
        int height;

        BufferedImage bufferedImage;
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imgData)) {
            bufferedImage = ImageIO.read(byteArrayInputStream);
        }
        width = bufferedImage.getWidth();
        height = bufferedImage.getHeight();

        if (angle != 0) {
            // 逆时针旋转化成顺时针
            angle = 360 - angle;
            // 将⻆角度转为弧度
            double theta = Math.toRadians(angle);
            // 确定旋转后的宽和⾼高
            int newWidth = (int) (Math.abs(Math.sin(theta)) * height + Math.abs(Math.cos(theta)) * width);
            int newHeight = (int) (Math.abs(Math.sin(theta)) * width + Math.abs(Math.cos(theta)) * height);
            BufferedImage spinImage = new BufferedImage(newWidth, newHeight, bufferedImage.getType());
            // 设置图⽚片背景颜⾊色
            Graphics2D gs = (Graphics2D) spinImage.getGraphics();
            gs.setColor(Color.white);
            // 以给定颜⾊色绘制旋转后图⽚片的背景,fillRect:对指定的矩形区域填充颜⾊色
            gs.fillRect(0, 0, newWidth, newHeight);
            // 确定原点坐标
            int x = (newWidth / 2) - (width / 2);
            int y = (newHeight / 2) - (height / 2);
            AffineTransform at = new AffineTransform();
            // 旋转图象
            at.rotate(theta, newWidth / 2.0, newHeight / 2.0);
            at.translate(x, y);
            AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
            spinImage = op.filter(bufferedImage, spinImage);
            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                ImageIO.write(spinImage, "jpg", out);
                imgData = out.toByteArray();
            } catch (IOException e) {
                throw new AppException("旋转图像失败", e);
            }
            width = newWidth;
            height = newHeight;
        }

        float pw = width;
        float ph = height;
        /// 计算pdf页面大小
        //        String pdfPageSize = myOcrConfig.getPdfPageSize();
        //        if (!StringUtils.isEmpty(pdfPageSize)) {
        //            Rectangle r = PageSize.getRectangle(pdfPageSize);
        //            if (width > height) {
        //                pw = r.getHeight();
        //                ph = r.getWidth();
        //            } else {
        //                ph = r.getHeight();
        //                pw = r.getWidth();
        //            }
        //        }

        // 设置pdf⼤小
        Rectangle rectangle = new Rectangle(pw, ph, 0);
        pdfDocument.setPageSize(rectangle);
        pdfDocument.open();
        PdfContentByte canvas = pdfWriter.getDirectContentUnder();
        com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(imgData);
        image.scaleAbsolute(rectangle.getWidth(), rectangle.getHeight());
        image.setAbsolutePosition(0, 0);
        // 加载背景图⽚片
        canvas.addImage(image);
    }

    private static void setTextToPdf(PdfWriter pdfWriter, List<PrismWordsInfo> prismWordsInfos, float opacity) {
        try {
            PdfContentByte canvas = pdfWriter.getDirectContentUnder();

            Rectangle rectangle = pdfWriter.getPageSize();

            // 获取原图和pdf的缩放⽐比例例
            float xScale = 1;
            float yScale = 1;

            // y轴坐标反转
            canvas.concatCTM(1f, 0f, 0f, 1f, 0f, rectangle.getHeight());
            // 解析图⽚片⽚片识别结果并⽣生⽣生成pdf
            // 系统字体对中⽂文⽀支持不不好，推荐更更换为其他字体，⽐比如simhei.ttf。 更更换⽅方式: 将
            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            // 设置⽂文字颜⾊色
            canvas.setColorFill(new Color(0xff, 0x00, 0x00));
            // 设置⽂文字透明度
            canvas.saveState();
            PdfGState state = new PdfGState();
            state.setFillOpacity(opacity);
            canvas.setGState(state);
            for (PrismWordsInfo prismWordsInfo : prismWordsInfos) {
                List<PrismWordsInfo.CharInfo> charInfos = prismWordsInfo.getCharInfo();

                for (PrismWordsInfo.CharInfo charInfo : charInfos) {
                    String word = charInfo.getWord();
                    // 全⻆半⻆转化
                    word = changePdfResult(word);
                    float x = charInfo.getX() / xScale;
                    float y = charInfo.getY() / yScale;
                    //                    float w = charInfo.getW() / xScale;
                    int h = (int) (charInfo.getH() / yScale);

                    if (h <= 0) {
                        List<PrismWordsInfo.Pos> posList = prismWordsInfo.getPos();
                        int minx = Integer.MAX_VALUE;
                        int miny = Integer.MAX_VALUE;
                        int maxx = Integer.MIN_VALUE;
                        int maxy = Integer.MIN_VALUE;
                        for (PrismWordsInfo.Pos pos : posList) {
                            minx = Math.min(minx, pos.getX());
                            miny = Math.min(miny, pos.getY());

                            maxx = Math.max(maxx, pos.getX());
                            maxy = Math.max(maxy, pos.getY());
                        }
                        h = maxy - miny;
                    }

                    // pdf增加⽂字
                    int fontSize = h;
                    if (fontSize < 0) {
                        log.warn("因文本大小为 " + fontSize + "，跳过文本 " + word + "，数据信息：" + JSON.toJSONString(charInfo));
                        continue;
                    }
                    canvas.beginText();
                    try {
                        canvas.setFontAndSize(baseFont, fontSize);
                        canvas.setHorizontalScaling(100);
                        canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, word, x, -(y + h), 0);
                    } catch (Exception e) {
                        log.warn("文字未被绘制到pdf上，失败原因：" + e.getMessage(), e);
                    } finally {
                        canvas.endText();
                    }
                }
            }
            canvas.restoreState();
        } catch (Exception e) {
            log.warn("pdf添加文字失败（生成单层pdf）", e);
        }
    }

    /**
     * 将字符串串中的全⻆角数字转化为半⻆角数字，标点转化为全⻆角
     *
     * @param src
     * @return
     */
    private static String changePdfResult(String src) {
        if (src == null) {
            return null;
        }
        char[] c = src.toCharArray();
        for (int i = 0; i < c.length; i++) {
            int c1 = c[i] - DBC_SBC_STEP;
            if (c1 >= 48 && c1 <= 57) {
                // 数字
                c[i] -= DBC_SBC_STEP;
            } else if (c1 >= 65 && c1 <= 90) {
                //大写字母
                c[i] -= DBC_SBC_STEP;
            } else if (c1 >= 97 && c1 <= 122) {
                // 小写字母
                c[i] -= DBC_SBC_STEP;
            }
        }
        return new String(c);
    }
}
